/*-------------------------------------------------------------------------
 *
 * tuptable.h
 *	  tuple table support stuff
 *
 *
 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/executor/tuptable.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef TUPTABLE_H
#define TUPTABLE_H

#include "access/htup.h"
#include "access/tupdesc.h"
#include "storage/buf.h"

/* POLAR px */
#include "access/memtup_px.h"
/* POLAR end */

/*----------
 * The executor stores tuples in a "tuple table" which is a List of
 * independent TupleTableSlots.  There are several cases we need to handle:
 *		1. physical tuple in a disk buffer page
 *		2. physical tuple constructed in palloc'ed memory
 *		3. "minimal" physical tuple constructed in palloc'ed memory
 *		4. "virtual" tuple consisting of Datum/isnull arrays
 *
 * The first two cases are similar in that they both deal with "materialized"
 * tuples, but resource management is different.  For a tuple in a disk page
 * we need to hold a pin on the buffer until the TupleTableSlot's reference
 * to the tuple is dropped; while for a palloc'd tuple we usually want the
 * tuple pfree'd when the TupleTableSlot's reference is dropped.
 *
 * A "minimal" tuple is handled similarly to a palloc'd regular tuple.
 * At present, minimal tuples never are stored in buffers, so there is no
 * parallel to case 1.  Note that a minimal tuple has no "system columns".
 * (Actually, it could have an OID, but we have no need to access the OID.)
 *
 * A "virtual" tuple is an optimization used to minimize physical data
 * copying in a nest of plan nodes.  Any pass-by-reference Datums in the
 * tuple point to storage that is not directly associated with the
 * TupleTableSlot; generally they will point to part of a tuple stored in
 * a lower plan node's output TupleTableSlot, or to a function result
 * constructed in a plan node's per-tuple econtext.  It is the responsibility
 * of the generating plan node to be sure these resources are not released
 * for as long as the virtual tuple needs to be valid.  We only use virtual
 * tuples in the result slots of plan nodes --- tuples to be copied anywhere
 * else need to be "materialized" into physical tuples.  Note also that a
 * virtual tuple does not have any "system columns".
 *
 * It is also possible for a TupleTableSlot to hold both physical and minimal
 * copies of a tuple.  This is done when the slot is requested to provide
 * the format other than the one it currently holds.  (Originally we attempted
 * to handle such requests by replacing one format with the other, but that
 * had the fatal defect of invalidating any pass-by-reference Datums pointing
 * into the existing slot contents.)  Both copies must contain identical data
 * payloads when this is the case.
 *
 * The Datum/isnull arrays of a TupleTableSlot serve double duty.  When the
 * slot contains a virtual tuple, they are the authoritative data.  When the
 * slot contains a physical tuple, the arrays contain data extracted from
 * the tuple.  (In this state, any pass-by-reference Datums point into
 * the physical tuple.)  The extracted information is built "lazily",
 * ie, only as needed.  This serves to avoid repeated extraction of data
 * from the physical tuple.
 *
 * A TupleTableSlot can also be "empty", holding no valid data.  This is
 * the only valid state for a freshly-created slot that has not yet had a
 * tuple descriptor assigned to it.  In this state, tts_isempty must be
 * true, tts_shouldFree false, tts_tuple NULL, tts_buffer InvalidBuffer,
 * and tts_nvalid zero.
 *
 * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot
 * code.  The caller of ExecSetSlotDescriptor() is responsible for providing
 * a descriptor that will live as long as the slot does.  (Typically, both
 * slots and descriptors are in per-query memory and are freed by memory
 * context deallocation at query end; so it's not worth providing any extra
 * mechanism to do more.  However, the slot will increment the tupdesc
 * reference count if a reference-counted tupdesc is supplied.)
 *
 * When tts_shouldFree is true, the physical tuple is "owned" by the slot
 * and should be freed when the slot's reference to the tuple is dropped.
 *
 * If tts_buffer is not InvalidBuffer, then the slot is holding a pin
 * on the indicated buffer page; drop the pin when we release the
 * slot's reference to that buffer.  (tts_shouldFree should always be
 * false in such a case, since presumably tts_tuple is pointing at the
 * buffer page.)
 *
 * tts_nvalid indicates the number of valid columns in the tts_values/isnull
 * arrays.  When the slot is holding a "virtual" tuple this must be equal
 * to the descriptor's natts.  When the slot is holding a physical tuple
 * this is equal to the number of columns we have extracted (we always
 * extract columns from left to right, so there are no holes).
 *
 * tts_values/tts_isnull are allocated when a descriptor is assigned to the
 * slot; they are of length equal to the descriptor's natts.
 *
 * tts_mintuple must always be NULL if the slot does not hold a "minimal"
 * tuple.  When it does, tts_mintuple points to the actual MinimalTupleData
 * object (the thing to be pfree'd if tts_shouldFreeMin is true).  If the slot
 * has only a minimal and not also a regular physical tuple, then tts_tuple
 * points at tts_minhdr and the fields of that struct are set correctly
 * for access to the minimal tuple; in particular, tts_minhdr.t_data points
 * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple.  This allows column
 * extraction to treat the case identically to regular physical tuples.
 *
 * tts_slow/tts_off are saved state for slot_deform_tuple, and should not
 * be touched by any other code.
 *----------
 */
typedef struct TupleTableSlot
{
	NodeTag		type;
	bool		tts_isempty;	/* true = slot is empty */
	bool		tts_shouldFree; /* should pfree tts_tuple? */
	bool		tts_shouldFreeMin;	/* should pfree tts_mintuple? */
#define FIELDNO_TUPLETABLESLOT_SLOW 4
	bool		tts_slow;		/* saved state for slot_deform_tuple */
#define FIELDNO_TUPLETABLESLOT_TUPLE 5
	HeapTuple	tts_tuple;		/* physical tuple, or NULL if virtual */
#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6
	TupleDesc	tts_tupleDescriptor;	/* slot's tuple descriptor */
	MemoryContext tts_mcxt;		/* slot itself is in this context */
	Buffer		tts_buffer;		/* tuple's buffer, or InvalidBuffer */
#define FIELDNO_TUPLETABLESLOT_NVALID 9
	int			tts_nvalid;		/* # of valid values in tts_values */
#define FIELDNO_TUPLETABLESLOT_VALUES 10
	Datum	   *tts_values;		/* current per-attribute values */
#define FIELDNO_TUPLETABLESLOT_ISNULL 11
	bool	   *tts_isnull;		/* current per-attribute isnull flags */
	MinimalTuple tts_mintuple;	/* minimal tuple, or NULL if none */
	HeapTupleData tts_minhdr;	/* workspace for minimal-tuple-only case */
#define FIELDNO_TUPLETABLESLOT_OFF 14
	uint32		tts_off;		/* saved state for slot_deform_tuple */
	bool		tts_fixedTupleDescriptor;	/* descriptor can't be changed */

	/* POLAR px: Mem tuple stuff */
	bool		tts_shouldFreeMem; /* should pfree tts_tuple? */
	MemTuple tts_memtuple;
	void 	*tts_mtup_buf;
	uint32  tts_mtup_buf_len;
	ItemPointerData tts_synthetic_ctid;	/* needed if memtuple is stored on disk */

	MemTupleBinding *tts_mt_bind;		/* mem tuple's binding */
	/* POLAR end */
} TupleTableSlot;

#define TTS_HAS_PHYSICAL_TUPLE(slot)  \
	((slot)->tts_tuple != NULL && (slot)->tts_tuple != &((slot)->tts_minhdr))

/*
 * TupIsNull -- is a TupleTableSlot empty?
 */
#define TupIsNull(slot) \
	((slot) == NULL || (slot)->tts_isempty)

/* in executor/execTuples.c */
extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);
extern TupleTableSlot *ExecAllocTableSlot(List **tupleTable, TupleDesc desc);
extern void ExecResetTupleTable(List *tupleTable, bool shouldFree);
extern TupleTableSlot *MakeSingleTupleTableSlot(TupleDesc tupdesc);
extern void ExecDropSingleTupleTableSlot(TupleTableSlot *slot);
extern void ExecSetSlotDescriptor(TupleTableSlot *slot, TupleDesc tupdesc);
extern TupleTableSlot *ExecStoreTuple(HeapTuple tuple,
			   TupleTableSlot *slot,
			   Buffer buffer,
			   bool shouldFree);
extern TupleTableSlot *ExecStoreMinimalTuple(MinimalTuple mtup,
					  TupleTableSlot *slot,
					  bool shouldFree);
extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreVirtualTuple(TupleTableSlot *slot);
extern TupleTableSlot *ExecStoreAllNullTuple(TupleTableSlot *slot);
extern HeapTuple ExecCopySlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot);
extern HeapTuple ExecFetchSlotTuple(TupleTableSlot *slot);
extern MinimalTuple ExecFetchSlotMinimalTuple(TupleTableSlot *slot);
extern Datum ExecFetchSlotTupleDatum(TupleTableSlot *slot);
extern HeapTuple ExecMaterializeSlot(TupleTableSlot *slot);
extern TupleTableSlot *ExecCopySlot(TupleTableSlot *dstslot,
			 TupleTableSlot *srcslot);

/* POLAR px */
extern TupleTableSlot *ExecStoreMemTuple(MemTuple tuple,
									  TupleTableSlot *slot,
									  bool shouldFree);
extern MemTuple ExecFetchSlotMemTuple(TupleTableSlot *slot);
extern GenericTuple ExecCopySlotGenericTuple(TupleTableSlot *slot);

static inline GenericTuple
ExecFetchSlotGenericTuple(TupleTableSlot *slot)
{
	Assert(!TupIsNull(slot));
	if (slot->tts_memtuple == NULL && slot->tts_tuple != NULL)
		return (GenericTuple) slot->tts_tuple;

	return (GenericTuple) ExecFetchSlotMemTuple(slot);
}
/* POLAR end */

/* in access/common/heaptuple.c */
extern Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull);
extern void slot_getallattrs(TupleTableSlot *slot);
extern void slot_getsomeattrs(TupleTableSlot *slot, int attnum);
extern bool slot_attisnull(TupleTableSlot *slot, int attnum);
extern bool slot_getsysattr(TupleTableSlot *slot, int attnum,
				Datum *value, bool *isnull);
extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum);

/* POLAR px */
static inline Datum *slot_get_values(TupleTableSlot *slot)
{
	return slot->tts_values;
}

static inline bool *slot_get_isnull(TupleTableSlot *slot)
{
	return slot->tts_isnull;
}

static inline void
free_memtuple(TupleTableSlot *slot)
{
	if (slot->tts_shouldFreeMem)
	{
		if(slot->tts_memtuple && slot->tts_memtuple != slot->tts_mtup_buf)
		{
			slot->tts_shouldFreeMem = false;
			pfree(slot->tts_memtuple);
		}
	}
	slot->tts_memtuple = NULL;
}

static inline void
free_mintuple(TupleTableSlot *slot)
{
	if (slot->tts_shouldFreeMin && slot->tts_mintuple)
		pfree(slot->tts_mintuple);
	slot->tts_shouldFreeMin = false;
	slot->tts_mintuple = NULL;
}

static inline void
free_htuple(TupleTableSlot *slot)
{
	if (slot->tts_shouldFree && slot->tts_tuple)
		pfree(slot->tts_tuple);
	slot->tts_shouldFree = false;
	slot->tts_tuple = NULL;
}

static inline bool
tup_has_memtuple(TupleTableSlot *slot)
{
	Assert(slot);
	return slot->tts_memtuple != NULL;
}

static inline TupleTableSlot *
ExecStoreGenericTuple(GenericTuple tup, TupleTableSlot *slot, bool shouldFree)
{
	if (is_memtuple(tup))
		return ExecStoreMemTuple((MemTuple) tup, slot, shouldFree);

	return ExecStoreTuple((HeapTuple) tup, slot, InvalidBuffer, shouldFree);
}
/* POLAR end */

#endif							/* TUPTABLE_H */
